package com.hongfan.gateway.filter;

import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hongfan.common.constants.AuthConstant;
import com.hongfan.common.model.ResultCode;
import com.hongfan.common.model.ResultMsg;
import com.hongfan.gateway.config.IgnoreUrlsConfig;
import org.apache.tomcat.util.json.TokenMgrError;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * 全局过滤器
 *
 * @author zcs
 * @date 2023/10/11
 */
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    @Resource
    TokenStore tokenStore;
    @Resource
    IgnoreUrlsConfig ignoreUrlsConfig;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String requestUrl = exchange.getRequest().getPath().value();
        //1、白名单放行，比如授权服务、静态资源.....
        if (checkUrls(ignoreUrlsConfig.getUrls(),requestUrl)){
            return chain.filter(exchange);
        }

        //2、 检查token是否存在
        String token = getToken(exchange);
        if (!StringUtils.hasText(token)) {
            return invalidTokenMono(exchange);
        }
        //3 判断是否是有效的token
        OAuth2AccessToken oAuth2AccessToken;
        try {
            //解析token，使用tokenStore
            oAuth2AccessToken = tokenStore.readAccessToken(token);
            Map<String, Object> objectMap = oAuth2AccessToken.getAdditionalInformation();
            //取出用户信息
            String user_name = objectMap.get("user_name").toString();
            //获取用户权限
            List<String> authorities = (List<String>) objectMap.get("authorities");
            JSONObject jsonObject = new JSONObject();
            jsonObject.put(AuthConstant.USERNAME, user_name);
            jsonObject.put(AuthConstant.AUTHORITIES, authorities);
            //将解析厚的token加密放入请求头部，方便下游微服务解析获取用户信息;
            String base64 = Base64.encode(jsonObject.toJSONString());
            ServerHttpRequest request = exchange.getRequest().mutate().header(AuthConstant.USER_TOKEN_HEADER, base64).build();
            exchange = exchange.mutate().request(request).build();
        } catch (InvalidTokenException e) {
            return Mono.error(e);
        }
        return chain.filter(exchange);
    }

    /**
     * 从请求头中获取Token
     */
    private String getToken(ServerWebExchange exchange) {
        String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (!StringUtils.hasText(tokenStr)) {
            return null;
        }
        String token = tokenStr.split(" ")[1];
        if (!StringUtils.hasText(token)) {
            return null;
        }
        return token;
    }

    /**
     * 对url进行校验匹配
     */
    private boolean checkUrls(List<String> urls, String path) {
        AntPathMatcher pathMatcher = new AntPathMatcher();
        for (String url : urls) {
            if (pathMatcher.match(url, path)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 无效的token
     */
    private Mono<Void> invalidTokenMono(ServerWebExchange exchange) {
        return buildReturnMono(ResultMsg.builder()
                .code(ResultCode.INVALID_TOKEN.getCode())
                .message(ResultCode.INVALID_TOKEN.getMsg())
                .build(), exchange);
    }


    private Mono<Void> buildReturnMono(ResultMsg resultMsg, ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        byte[] bits = JSON.toJSONString(resultMsg).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset:utf-8");
        return response.writeWith(Mono.just(buffer));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
